using System;
using System.Data;
using System.Collections.Generic;
using System.Text;

using AIFSContentManagementSystem;
using AIFSCMSDataAccessLayer.Dao;
using AIFSContentManagementSystem.DomainObjects;
using AIFSContentManagementSystem.DomainObjects.Mapper;
using AIFSContentManagementSystem.ArticleManagement.Messages;
using AIFSContentManagementSystem.CriteriaManagement;
using AIFSContentManagementSystem.CriteriaManagement.Messages;
using AIFSDataAccessLayer.Utilities;

namespace AIFSContentManagementSystem.ArticleManagement
{
    public sealed class ArticleManager : IArticleManager
    {
        public GetArticleListResponse Process(GetArticleListRequest request)
        {
            GetArticleListResponse response = new GetArticleListResponse();

            if (request.ProgramName == null)
                response.AddErrorMessage("Program name is required");

            if (!request.ProgramName.Equals("COLLEGE"))
                response.AddErrorMessage("Invalid program name");

            if (request.ArticleType == ArticleType.UNSET)
                response.AddErrorMessage("Article type is required");

            if (response.HasError)
                return response;

            //Step 1 get the article entity and map
            DataTable dtArticleEntity = this.ArticleDao.GetArticles(Enum.GetName(typeof(ArticleType), request.ArticleType), request.ProgramName, request.FlaggedAsActive);

            if (dtArticleEntity == null || dtArticleEntity.Rows.Count == 0)
            {
                return response;
            }

            ArticleMapper articleMapper = new ArticleMapper();
            List<Article> articles = articleMapper.Map(dtArticleEntity);

            //List of filtered articles after applying criteria
            List<Article> filteredArticles = new List<Article>();
            
            //Step 2 Get criteria for articles
            ICriteriaManager criteriaManager = this.ManagerContext.GetCriteriaManager();

            ArticleCriteriaMapper articleCriteriaMapper = new ArticleCriteriaMapper();

            foreach (Article article in articles)
            {
                DataTable dtArticleCriteria = this.ArticleDao.GetCriteriaForArticle(article.ArticleId);

                if (dtArticleCriteria != null && dtArticleCriteria.Rows.Count != 0)
                {
                    List<ArticleCriteria> articleCriteriaList = articleCriteriaMapper.Map(dtArticleCriteria);

                    foreach (ArticleCriteria articleCriterion in articleCriteriaList)
                    {
                        GetCriteriaRequest getCriteriaReq = new GetCriteriaRequest();
                        getCriteriaReq.CriteriaId = articleCriterion.CriteriaId;

                        GetCriteriaResponse getCriteriaRes = criteriaManager.GetCriteria(getCriteriaReq);
                        if (getCriteriaRes.HasError)
                        {
                            response.CopyErrorMessageFrom(getCriteriaRes);
                            return response;
                        }

                        if (getCriteriaRes.HasDataInReturn)
                        {
                            article.Criteria.Add(getCriteriaRes.Criterion);
                        }
                    }
                }

                //Assert the criteria with the requested criteria, if it matches add to the filtered list
                bool assertResult = false;
                if (article.Criteria.Count == 0) //if expected article criteria is empty no filter is necessary
                {
                    assertResult = true;
                }
                else
                {
                    ICriteriaAsserter criteriaAssertion = this.ManagerContext.GetCriteriaManager().CriteriaAsserter;
                    assertResult = criteriaAssertion.IsMatch(request.ArticleCriteria, request.OrArticleCriteriaList, article.Criteria);                    
                }

                //Once a filtered article is found parse for place holder and add to the final list
                if (assertResult)
                    filteredArticles.Add(article);
            }

            //Step 4 Get fragments for this article
            ArticleFragmentMapper articleFragmentMapper = new ArticleFragmentMapper();
            FragmentMapper fragmentMapper = new FragmentMapper();

            foreach (Article article in filteredArticles)
            {
                DataTable dtArticleFragment = this.ArticleDao.GetFragmentForArticle(article.ArticleId);
                List<ArticleFragment> articleFragments = articleFragmentMapper.Map(dtArticleFragment);

                foreach (ArticleFragment articleFragment in articleFragments)
                {
                    DataTable dtFragment = this.FragmentDao.GetFragment(articleFragment.FragmentId);
                    if (dtFragment != null && dtFragment.Rows.Count != 0)
                    {
                        Fragment fragment = fragmentMapper.Map(dtFragment.Rows[0]);
                        article.Fragments.Add(fragment);
                    }
                }

                //Parse the fragments to retrieve place holders
                this.Parser.Parse(article);
            }

            response.AddArticle(filteredArticles);

            return response;
        }

        #region IManager Members
        IManagerContext managerConext;
        public IManagerContext ManagerContext
        {
            get
            {
                return managerConext;
            }
            set
            {
                managerConext = value;
            }
        }

        #endregion

        #region IArticleManager Members

        IArticleDao articleDao;
        public IArticleDao ArticleDao
        {
            get
            {
                return articleDao;
            }
            set
            {
                articleDao = value;
            }
        }

        IFragmentDao fragmentDao;
        public IFragmentDao FragmentDao
        {
            get
            {
                return fragmentDao;
            }
            set
            {
                fragmentDao = value;
            }
        }

        IParser parser;
        public IParser Parser
        {
            get
            {
                return parser;
            }
            set
            {
                parser = value;
            }
        }

        #endregion
    }
}
